home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1997 February: Tool Chest / Dev.CD Feb 97 TC.toast / Sample Code / Networking / OTTraceRouteSample / OTTraceRouteSample.c next >
Encoding:
C/C++ Source or Header  |  1996-06-28  |  8.4 KB  |  308 lines  |  [TEXT/CWIE]

  1. /*
  2.     File:                OTTraceRouteSample.c
  3.  
  4.     Contains:        A trivial traceroute implementation.
  5.  
  6.     Written by:    Quinn "The Eskimo!"
  7.  
  8.     Copyright:    © 1996 by Apple Computer, Inc., all rights reserved.
  9.  
  10.     Change History (most recent first):
  11.  
  12.     You may incorporate this sample code into your applications without
  13.     restriction, though the sample code has been provided "AS IS" and the
  14.     responsibility for its operation is 100% yours.  However, what you are
  15.     not permitted to do is to redistribute the source as "DSC Sample Code"
  16.     after having made changes. If you're going to re-distribute the source,
  17.     we require that you make it clear in the source that the code was
  18.     descended from Apple Sample Code, but that you've made changes.
  19. */
  20.  
  21. #include <stdio.h>
  22. #include <OpenTransport.h>
  23. #include <OpenTptInternet.h>
  24.  
  25. /////////////////////////////////////////////////////////////////////
  26.  
  27. static OSStatus CreateAndConfigUDP(EndpointRef *ep)
  28. {
  29.     OSStatus err;
  30.     
  31.     *ep = OTOpenEndpoint(OTCreateConfiguration(kUDPName), 0, nil, &err);
  32.     
  33.     if (err == noErr) {
  34.     err = OTBind(*ep, nil, nil);
  35.     
  36.         // no others options to negotiate at this stage
  37.     }
  38.     
  39.     return (err);
  40. }
  41.  
  42. /////////////////////////////////////////////////////////////////////
  43.  
  44. static OSStatus CreateAndConfigICMP(EndpointRef *ep)
  45. {
  46.     OSStatus err;
  47.     
  48.     *ep = OTOpenEndpoint(OTCreateConfiguration(kRawIPName), 0, nil, &err);
  49.     
  50.     if (err == noErr) {
  51.     err = OTBind(*ep, nil, nil);
  52.     
  53.         // no others options to negotiate at this stage
  54.         
  55.         // You might think we need to negotiate the XTI_GENERIC/XTI_PROTOTYPE
  56.         // option to request ICMP packets (ie protocol 2).  This is not
  57.         //  necessary because rawip endpoints default to that protocol.
  58.     }
  59.     
  60.     return (err);
  61. }
  62.  
  63. /////////////////////////////////////////////////////////////////////
  64.  
  65. static OSStatus DoNegotiateIP_TTLOption(EndpointRef ep, long ttl)
  66. {
  67.     // According to the XTI spec, IP_TTL is an INET_IP level option that
  68.     //  determines the TTL of an IP packet.  The value of this option is
  69.     //  a UInt8.  This routine simply negotiates that option on the ep
  70.     //  endpoint.
  71.     OSStatus    err;
  72.     TOption*    opt;                                                // points to buf, makes it easier to access
  73.     TOptMgmt    req;
  74.     TOptMgmt    ret;
  75.     UInt8            buf[kOTFourByteOptionSize];    // define buffer for options, although we only
  76.                                                                                 // use a "1 byte option", we define a "4 byte option"
  77.                                                                                 // buffer to hold the returning options
  78.     
  79.     // Point opt to the start of buf.  This allows us to set the items in buf easily.
  80.     opt = (TOption*)buf;
  81.  
  82.     // Setup the fields of the options buffer...
  83.     
  84.     opt->level    = INET_IP;
  85.     opt->name    = IP_TTL;
  86.     opt->len    = kOTOneByteOptionSize;            // Note that kOTOneByteOptionSize != 1, it also
  87.     opt->status = 0;                                            //  includes the size of the option header.
  88.     *(UInt8*)opt->value = ttl;
  89.  
  90.     // Set up the req structure to denote the options we're requesting...
  91.     
  92.     req.opt.buf    = buf;
  93.     req.opt.len    = kOTOneByteOptionSize;
  94.     req.flags    = T_NEGOTIATE;
  95.  
  96.     // Set up the ret structure to hold the options we got...
  97.     
  98.     ret.opt.buf = buf;
  99.     ret.opt.maxlen = kOTFourByteOptionSize;
  100.  
  101.     err = OTOptionManagement(ep, &req, &ret);
  102.     
  103.     // If no error then return the option status value...
  104.     
  105.     if (err == kOTNoError) {
  106.         if (opt->status != T_SUCCESS)
  107.             err = opt->status;
  108.         else
  109.             err = kOTNoError;
  110.     }
  111.                 
  112.     return (err);
  113. }
  114.  
  115. /////////////////////////////////////////////////////////////////////
  116.  
  117. // this is the data we send in our UDP packets...
  118.  
  119. static unsigned char udp_data[8] = {0, 1, 2, 3, 4, 5, 6, 7};
  120.  
  121. /////////////////////////////////////////////////////////////////////
  122.  
  123. static OSStatus SendUDPWithTTL(EndpointRef ep, InetHost dest, long ttl)
  124. {
  125.     OSStatus err;
  126.     InetAddress dest_addr;
  127.     TUnitData udata;
  128.     OTResult look;
  129.     
  130.     err = DoNegotiateIP_TTLOption(ep, ttl);
  131.  
  132.     if (err == noErr) {
  133.  
  134.         OTInitInetAddress(&dest_addr, 33434, dest);        
  135.  
  136.         // 33434 is the default port for unix traceroute.
  137.         //  It was chosen because it's unlikely that anyone will be listening on this
  138.         //  port.  Hence any packets that make it through will generate an ICMP
  139.         //  port unreachable error.
  140.  
  141.         udata.addr.len = sizeof(dest_addr);
  142.         udata.addr.buf = (unsigned char *) &dest_addr;
  143.         
  144.         udata.opt.len = 0;
  145.         udata.opt.buf = nil;
  146.         
  147.         udata.udata.len = sizeof(udp_data);
  148.         udata.udata.buf = &udp_data[0];
  149.  
  150.         // The act of sending is a little more complicated than it should be.
  151.         //  Basically the ICMP errors that come back from all these bogus (short TTL)
  152.         //  packets that I send, end up as datagram errors on the sending endpoint.
  153.         //  If you attempt to send with a T_UDERR sitting on the endpoint, you get
  154.         //  a kOTLookErr.
  155.         //
  156.         // I addresses this by junking the error and looping when I get a T_UDERR.
  157.         
  158.         do {
  159.             err = OTSndUData(ep, &udata);
  160.             if (err == kOTLookErr) {
  161.                 look = OTLook(ep);
  162.                 if (look == T_UDERR) {
  163.                     printf("•Junking T_UDERR.\n");
  164.                     fflush(stdout);
  165.                     (void) OTRcvUDErr(ep, nil);        // clear the error condition without receiving the error info
  166.                     err = 666;                                        // and attempt to send again
  167.                     // Yeah, yeah, I know that using error codes to control program flow is bad
  168.                     //  style.  Hey, I was in hurry!        
  169.                 }
  170.             }
  171.         } while (err == 666);
  172.     }
  173.  
  174.     return (err);
  175. }
  176.  
  177. /////////////////////////////////////////////////////////////////////
  178.  
  179. // we use this buffer to hold incoming ICMP packets
  180.  
  181. static UInt8 icmp_data[5000];
  182.  
  183. /////////////////////////////////////////////////////////////////////
  184.  
  185. static OSStatus WaitAndPrintICMPs(EndpointRef ep, Boolean *done)
  186. {
  187.     TUnitData udata;
  188.     long start_time;
  189.     OSStatus err;
  190.     InetAddress src_addr;
  191.     
  192.     start_time = TickCount();
  193.     
  194.     // Wait for 3 seconds and print out any ICMP packets we get back.
  195.     
  196.     do {
  197.  
  198.         // Set up the received...
  199.         udata.addr.buf = (UInt8*) &src_addr;
  200.         udata.addr.maxlen = sizeof(struct InetAddress);
  201.         udata.opt.buf = nil;
  202.         udata.opt.maxlen = 0;
  203.         udata.udata.buf = icmp_data;
  204.         udata.udata.maxlen = sizeof(icmp_data);
  205.         
  206.         // Look for a packet...
  207.         
  208.         err = OTRcvUData(ep, &udata, nil);
  209.         if (err == noErr) {
  210.             // Print out salient information from the packet...
  211.             printf("•••Got packet!•••\n");
  212.             
  213.             printf("ICMP from = %d.%d.%d.%d\n", icmp_data[12], icmp_data[13], icmp_data[14], icmp_data[15]);
  214.             printf("ICMP type = %d\n", icmp_data[20]);
  215.             printf("ICMP code = %d\n", icmp_data[21]);
  216.             
  217.             // Stop if the traceroute is at an end.  Note that this code assumes that
  218.             //  the ICMP header will start 20 bytes into the packet.  This is correct
  219.             //  for 99% of IP packets, but not correct in general.  If the IP packet
  220.             //  has IP level options, they will be inserted between the 20 byte IP
  221.             //  header and the payload, thereby stuffing up this calculation.  I was
  222.             //  slack and ignored this issue.  You should not!
  223.             
  224.             if (icmp_data[20] == 3 && icmp_data[20] == 3) {
  225.                 // type 3 = destination unreachable
  226.                 // code 3 = port unreachable
  227.                 // These two imply that we're trying to deliver the packet on the destination
  228.                 //  host, and it couldn't be delivered because the port is wrong.  The fact
  229.                 //  that we're hitting the destination host means we can stop the trace.
  230.                 *done = true;
  231.             }
  232.             
  233.             fflush(stdout);
  234.         } else if (err == kOTNoDataErr) {
  235.             err = noErr;
  236.         }
  237.     } while (err == noErr && TickCount() < start_time + 3 * 60 && !*done);
  238.  
  239.     return (err);
  240. }
  241.  
  242. /////////////////////////////////////////////////////////////////////
  243.  
  244. static OSStatus DoTraceRoute(InetHost dest)
  245. {
  246.     OSStatus err;
  247.     EndpointRef udp_ep = nil;
  248.     EndpointRef icmp_ep = nil;
  249.     long ttl;
  250.     Boolean done;
  251.     
  252.     // Create the endpoints and negotiate the options...
  253.     err = CreateAndConfigUDP(&udp_ep);
  254.     if (err == noErr) {
  255.         err = CreateAndConfigICMP(&icmp_ep);
  256.     }
  257.     
  258.     // Do the main traceroute loop...
  259.     
  260.     ttl = 1;
  261.     done = false;
  262.     do {
  263.         printf("\nSending with TTL = %d.\n", ttl);
  264.         err = SendUDPWithTTL(udp_ep, dest, ttl);
  265.         if (err == noErr) {
  266.             err = WaitAndPrintICMPs(icmp_ep, &done);
  267.         }
  268.         if (err == noErr) {
  269.             ttl += 1;
  270.         }
  271.     } while (err == noErr && ttl < 30 && !done);
  272.  
  273.     if (done) {
  274.         printf("Traceroute completed successfully!\n");
  275.     }
  276.  
  277.     // clean up
  278.     if (udp_ep != nil) {
  279.         (void) OTCloseProvider(udp_ep);
  280.     }
  281.     if (icmp_ep != nil) {
  282.         (void) OTCloseProvider(icmp_ep);
  283.     }
  284.     return err;
  285. }
  286.  
  287. /////////////////////////////////////////////////////////////////////
  288.  
  289. void main(void) {
  290.     OSStatus err;
  291.     
  292.     printf("Hello Cruel World!\n");
  293.     
  294.     err = InitOpenTransport();
  295.     if (err == noErr) {
  296.         
  297.         err = DoTraceRoute(0x822B0202);            // apple.com
  298.     
  299.         CloseOpenTransport();
  300.     }
  301.     
  302.     if (err == noErr) {
  303.         printf("Success!\n");
  304.     } else {
  305.         printf("Failure!  Error = %d.\n", err);
  306.     }
  307.     printf("Done.  Press command-Q to Quit.\n");
  308. }